package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.NewsUpOrDownConstants;
import com.heima.common.constants.message.PublishArticleConstants;
import com.heima.common.constants.wemedia.NewsAutoScanConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustException;
import com.heima.model.admin.dtos.NewsAuthDTO;
import com.heima.model.admin.vos.WmNewsVO;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.WmNewsDTO;
import com.heima.model.wemedia.dtos.WmNewsPageReqDTO;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.plugin2.message.Message;
import sun.plugin2.message.Serializer;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.hankcs.hanlp.corpus.tag.Nature.wm;

/**
 * @author lo
 * @date 2021/9/23
 */
@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    //访问前缀
    @Value("${file.oss.web-site}")
    String site;

    @Autowired
    WmMaterialMapper wmMaterialMapper;

    @Autowired
    WmNewsMaterialMapper wmNewsMaterialMapper;

    @Autowired
    RabbitTemplate rabbitTemplate;


    /**
     * 分页查询文章列表
     *
     * @param dto WmNewsPageReqDTO 分页查询参数
     * @return
     */
    @Override
    public ResponseResult getPageList(WmNewsPageReqDTO dto) {
        //1.校验参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.构建分页查询条件
        LambdaQueryWrapper<WmNews> wrapper = Wrappers.lambdaQuery();
        //标题文章根据关键词查询
        wrapper.like(Strings.isNotBlank(dto.getKeyword()), WmNews::getTitle, dto.getKeyword());
        //频道信息查询
        wrapper.eq(dto.getChannelId() != null, WmNews::getChannelId, dto.getChannelId());
        //根据状态
        wrapper.eq(dto.getStatus() != null, WmNews::getStatus, dto.getStatus());
        //开始时间
        wrapper.ge(dto.getBeginPubDate() != null, WmNews::getPublishTime, dto.getBeginPubDate());
        //结束时间
        wrapper.le(dto.getEndPubDate() != null, WmNews::getPublishTime, dto.getEndPubDate());

        //3.获取当前用户id
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_STATUS_ERROR, "请重新登录");
        }
        wrapper.eq(WmNews::getUserId, user.getId());

        //4.按照创建时间降序排序
        wrapper.orderByDesc(WmNews::getCreatedTime);

        IPage<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        this.page(page, wrapper);

        PageResponseResult result = new PageResponseResult(dto.getPage(), dto.getSize(), page.getTotal(), page.getRecords());
        // 图片处理设置的host为webSite前缀，原因是前端代码已处理图片前缀。
        result.setHost(site);
        return result;
    }

    /**
     * 自媒体文章发布
     *
     * @param dto 前端发送的文章全部信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseResult submitNews(WmNewsDTO dto) {
        //1.检查参数
        if (dto == null || Strings.isBlank(dto.getContent())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "文章请填写内容");
        }
        //2.获取当前用户
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "登录超时,请重新登陆");
        }
        //3.封装wmNewS
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto, wmNews);
        //4.判断封面类型
        if (wmNews.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            //因为数据库里面的type不能为负数自动封面类型为 -1
            //tinyint 表示的范围是0-255之间的数字
            wmNews.setType(null);
        }
        // 处理dto参数 images封面集合 转换成 字符串
        String images = imageListToStr(dto.getImages(), site);

        //将图片字符串存入wmNews对象
        wmNews.setImages(images);
        //存入用户id
        wmNews.setUserId(user.getId());
        /**
         * 5.修改或者保存文章
         */
        saveOrUpdateWmNews(wmNews);
        /**
         * 6.文章内容中抽取图片列表
         */
        List<String> imageList = parseContentlMages(dto);

        //7.判断是不是草稿
        if (dto.getStatus().equals(WemediaConstants.WM_NEWS_DRAFT_STATUS)) {
            //是草稿 直接返回
            return ResponseResult.okResult();
        }

        //8. 判断是否是提交状态
        if (WmNews.Status.SUBMIT.getCode() == dto.getStatus()) {
            //文章图片不为空
            if (imageList != null) {
                // 存储文章图片与文章对应的关系
                saveRelativeInfoForContent(imageList, wmNews.getId(), WemediaConstants.WM_CONTENT_REFERENCE);
            }
            //9.存储封面图片与文章对应的关系
            saveRelativeInfoForConver(dto, imageList, wmNews);
        }

        //发送自动审核消息
        // 3.4 发送待审核消息
        rabbitTemplate.convertAndSend(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_QUEUE, wmNews.getId());
        log.debug("成功发送 待审核消息 ==> 队列:{}, 文章id:{}", NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_QUEUE, wmNews.getId());
        return ResponseResult.okResult();
    }

    /**
     * @param dto       传递过来的文章信息
     * @param imageList 封面中的图片
     * @param wmNews    文章信息
     */
    private void saveRelativeInfoForConver(WmNewsDTO dto, List<String> imageList, WmNews wmNews) {

        List<String> images = dto.getImages();

        // 自动获取封面 ****
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
            int size = imageList.size();
            if (size > 0 && size <= 2) {
                //单图封面
                images = imageList.stream().limit(1).collect(Collectors.toList());
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
            } else if (size > 2) {
                //三图封面
                images = imageList.stream().limit(1).collect(Collectors.toList());
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
            } else {
                //没有封面
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            if (images != null && images.size() > 0) {
                // 将图片集合 转为字符串  url1,url2,url3
                wmNews.setImages(imageListToStr(images, site));
            }
            //更新文章数据
            updateById(wmNews);
        }
        // 保存图片列表和素材的关系
        if (images != null && images.size() != 0) {
            List<String> list = images.stream().map(mag -> mag.replaceAll(site, ""))
                    .collect(Collectors.toList());
            //调用保存方法 将封面图片 和 文章的关系 存入关联表
            saveRelativeInfoForContent(list, wmNews.getId(), WemediaConstants.WM_IMAGE_REFERENCE);
        }
    }

    /**
     * 抽取图片与素材关联关系
     *
     * @param imageList 图片列表
     * @param wmNewsId  文章id
     * @param type      引用类型
     */
    private void saveRelativeInfoForContent(List<String> imageList, Integer wmNewsId, Short type) {
        //1.去素材表查询对应图片的id
        //构建查询条件 根据图片路径 和 用户id进行查询
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery()
                .in(CollectionUtils.isNotEmpty(imageList), WmMaterial::getUrl, imageList)
                .eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId()));
        //2 校验素材列表
        if (wmMaterials == null || wmMaterials.size() == 0) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //将其收集为一个map集合 键是url 值是对应的id
        Map<String, Integer> map = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));

        ArrayList<Integer> list = new ArrayList<>();
        for (WmMaterial wmMaterial : wmMaterials) {
            Integer id = map.get(wmMaterial.getUrl());
            //说明图片已经被删除
            if (id == null) {
                CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            list.add(id);
        }
        //3.向素材关联表保存数据
        wmNewsMaterialMapper.saveRelations(list, wmNewsId, type);
    }

    /**
     * 文章内容中抽取图片列表
     *
     * @param dto 前端传递的参数
     * @return
     */
    private List<String> parseContentlMages(WmNewsDTO dto) {
        //1.将json字符串转换为一个map集合
        List<Map> mapList = JSON.parseArray(dto.getContent(), Map.class);
        //2.判断其类型
        List<String> imageList = mapList.stream().filter(map -> map.get("type").equals(WemediaConstants.WM_NEWS_TYPE_IMAGE))
                //获取到value下面的图片路径
                .map(value -> (String) value.get("value"))
                //将图片的url去前缀
                .map(url -> url.replaceAll(site, ""))
                //收集为一个list集合
                .collect(Collectors.toList());
        return imageList;
    }

    /**
     * 修改或者保存文章
     *
     * @param wmNews
     */
    private void saveOrUpdateWmNews(WmNews wmNews) {
        if (wmNews.getId() == null) {
            wmNews.setSubmitedTime(new Date());
            wmNews.setCreatedTime(new Date());
            //说明是保存文章
            this.save(wmNews);
        } else {
            //修改文章
            //删除素材与文章的关系
            wmNewsMaterialMapper.deleteById(wmNews.getId());
            this.updateById(wmNews);
        }
    }


    /**
     * 图片列表转字符串，并去除图片前缀
     *
     * @param images  图片列表
     * @param webSite 图片前缀
     */
    private String imageListToStr(List<String> images, String webSite) {
        // 获取流
        return images.stream()
                // 对流数据的中间操作
                .map((url) -> url.replace(webSite, ""))
                .collect(Collectors.joining(","));
    }

    /**
     * 根据id查询文章
     *
     * @param id 文章id
     * @return
     */
    @Override
    public ResponseResult findWmNewsById(Integer id) {
        //1.校验参数
        if (id == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //2.根据id查询对应的文章
        WmNews wmNews = this.getById(id);
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        ResponseResult result = ResponseResult.okResult(wmNews);
        result.setHost(site);
        return result;
    }

    /**
     * 删除文章
     *
     * @param id 文章id
     * @return
     */
    @Override
    public ResponseResult delNews(Integer id) {
        /**
         * - 当文章状态为9(已发布)且已上架则不能删除文章，下架状态可以删除，如果是其他状态可以删除
         * - 删除文章之前需要先把素材与文章的关系删除掉
         */

        //1.校验参数
        if (id == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.根据id查询文章
        WmNews wmNews = getById(id);
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //- 当文章状态为9(已发布)且已上架则不能删除文章，下架状态可以删除，如果是其他状态可以删除
        if (wmNews.getStatus().equals(WemediaConstants.WM_NEWS_PUBLISH_STATUS)
                && wmNews.getEnable().equals(WemediaConstants.WM_NEWS_UP)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_ALLOW, "请下架后再删除");
        }
        // - 删除文章之前需要先把素材与文章的关系删除掉
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                .eq(wmNews.getId() != null, WmNewsMaterial::getNewsId, wmNews.getId()));

        //删除文章
        this.removeById(id);
        return ResponseResult.okResult();
    }

    /**
     * 文章上下架
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDTO dto) {
        //校验参数
        if (dto == null || dto.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //根据id查询对应的文章数据
        WmNews wmNews = getById(dto.getId());
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        Short enable = dto.getEnable();
        if (enable == null || enable.equals(WemediaConstants.WM_NEWS_UP) && enable.equals(WemediaConstants.WM_NEWS_DOWN)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "上下架状态错误");
        }

        //判断文章是否发布
        if (!wmNews.getStatus().equals(WemediaConstants.WM_NEWS_PUBLISH_STATUS)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章没有发布");
        }
        //更新文章状态
        this.update(Wrappers.<WmNews>lambdaUpdate()
                .eq(WmNews::getId, wmNews.getId())
                .set(WmNews::getEnable, dto.getEnable())
        );

        //TODO 上架配置到文章配置表中
        if (wmNews.getArticleId()!=null) {
            if(enable.equals(WemediaConstants.WM_NEWS_UP)){
                // 上架消息
                rabbitTemplate.convertAndSend(NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE,
                        NewsUpOrDownConstants.NEWS_UP_ROUTE_KEY,wmNews.getArticleId());
            }else {
                // 下架消息
                rabbitTemplate.convertAndSend(NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE,
                        NewsUpOrDownConstants.NEWS_DOWN_ROUTE_KEY,wmNews.getArticleId());
            }
        }
        //TODO 上架将将文章发布到es上
        return ResponseResult.okResult();
    }

    @Autowired
    WmNewsMapper wmNewsMapper;

    /**
     * 查询文章列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findList(NewsAuthDTO dto) {
        //1.检查参数
        dto.checkParam();
        //记录当前页
        int currentPage = dto.getPage();
        //设置起始页
        dto.setPage((dto.getPage() - 1) * dto.getSize());
        if (StringUtils.isNotBlank(dto.getTitle())) {
            dto.setTitle("%" + dto.getTitle() + "%");
        }

        //2.分页查询
        List<WmNewsVO> wmNewsVoList = wmNewsMapper.findListAndPage(dto);
        //统计多少条数据
        long count = wmNewsMapper.findListCount(dto);

        //3.结果返回
        ResponseResult result = new PageResponseResult(currentPage, dto.getSize(), count, wmNewsVoList);
        result.setHost(site);
        return result;
    }

    @Autowired
    WmUserService wmuser;

    /**
     * 人工审核模块
     * 根据id 查询 文章信息
     *
     * @param id 文章id
     * @return
     */
    @Override
    public ResponseResult findWmNewsVo(Integer id) {
        //1.校验参数
        if (id == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //查询对应的文章信息
        WmNews wmNews = this.getById(id);
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "对应的文章不存在");
        }
        WmUser wmUser = this.wmuser.getById(wmNews.getUserId());

        WmNewsVO wmNewsVO = new WmNewsVO();
        //封装对象
        BeanUtils.copyProperties(wmNews, wmNewsVO);
        if (wmUser.getName() != null) {
            wmNewsVO.setAuthorName(wmUser.getName());
        }
        ResponseResult responseResult = ResponseResult.okResult(wmNewsVO);
        responseResult.setHost(site);
        return responseResult;
    }

    /**
     * 人工审核
     *
     * @param status 2  审核失败  4 审核成功
     * @param dto
     * @return
     */
    @Override
    public ResponseResult updateStatus(Short status, NewsAuthDTO dto) {
        //1.检查参数状态
        if (status == null || dto.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.根据id查询对应的文章
        WmNews wmNews = getById(dto.getId());
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "对应的文章不存在");
        }
        // 检查文章状态 不能为9  已发布
        if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW, "文章已发布");
        }

        //3.改变文章状态
        wmNews.setStatus(status);
        if (StringUtils.isNotBlank(dto.getMsg())) {
            wmNews.setReason(dto.getMsg());
        }
        //4.更新文章状态
        this.updateById(wmNews);

        //5 TODO 审核成功后进行自动文章发布
        if (wmNews.getStatus().equals(WemediaConstants.WM_NEWS_MANPOWER_PASS_STATUS)) {
            // 获取发布时间的毫秒值
            Date publishTime = wmNews.getPublishTime();
            //装换为毫秒值
            long publish = publishTime.getTime();
            //2.获取当前时间
            long time = System.currentTimeMillis();
            long remainTime = publish - time;
            log.debug("开始发送发布文章的信息");
            if (remainTime > 0) {
                //说明文章还没到发布时间,延迟发送
                rabbitTemplate
                        .convertAndSend(
                                //指定交换机
                                PublishArticleConstants.PUBLISH_ARTICLE_EXCHANGE,
                                //指定routingKey
                                PublishArticleConstants.PUBLISH_ARTICLE_DEAD_ROUTE_KEY,
                                //发送的信息
                                wmNews.getId(),
                                //设定延迟时间
                                (message) -> {
                                    message.getMessageProperties().setExpiration(String.valueOf(publish));
                                    return message;
                                });
                log.debug("延时发布文章通知成功发送，文章id : {} , 延时时间: {}毫秒", wmNews.getId(), remainTime);
            } else {
                //说明文章的发布时间已经到达,设置延迟时间为0
                rabbitTemplate.convertAndSend(
                        PublishArticleConstants.PUBLISH_ARTICLE_EXCHANGE,
                        PublishArticleConstants.PUBLISH_ARTICLE_DEAD_ROUTE_KEY,
                        wmNews.getId(),
                        message -> {
                            message.getMessageProperties().setExpiration("0");
                            return message;
                        }
                );
                log.debug("发布文章通知成功发送，不延时,文章id : {} , 延时时间: {}毫秒", wmNews.getId(), remainTime);
            }
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
}
